﻿//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Licensed under the MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////

#include "UnitHelper.h"

#include "TranslatorManager.h"

#include "EnableCompileWarning_The_LAST_IncludeInCpp.h"

using namespace gcmp;

std::wstring UnitMap::GetErrorStr() const
{
    return m_errorStr;
}

void UnitMap::SetErrorStr(const std::wstring& val)
{
    m_errorStr = val;
}

UnitMap::UnitMap()
{
}

UnitMap::~UnitMap()
{
}

UnitMap::UnitMap(const ExprUnitType& type)
{
    InitialFrom(type);
}

UnitMap::UnitMap(const UnitMap& types)
{
    this->GetCopyFrom(types);
}

UnitMap& UnitMap::operator=(const UnitMap& types)
{
    this->GetCopyFrom(types);
    return *this;
}

bool UnitMap::IsZero(const double& value)const
{
    if (abs(value) < PRECISION_DEF)
        return true;
    return false;
}

bool UnitMap::InitialFrom(const ExprUnitType& type, const double& value)
{
    if (!IsCalculatable(type))
        return false;
    Clear();
    m_units.insert(std::pair<ExprUnitType, double>(type, value));
    return true;
}

bool UnitMap::IsValid() const
{
    // bool型、字符串型的m_units为空，应该认为是有效的
    if (m_units.size() == 0)
        return true;
    for (auto unit = m_units.begin(); unit != m_units.end(); unit++)
    {
        ExprUnitType type = unit->first;
        if (!IsCalculatable(type) && !IsZero(unit->second))
            return false;
    }
    return true;
}

bool UnitMap::IsEmpty() const
{
    return m_units.empty();
}

bool UnitMap::IsNum() const
{
    if (m_units.size() == 1)
    {
        for (auto unit = m_units.begin(); unit != m_units.end(); unit++)
        {
            if (unit->first == ExprUnitType::Num)
                return true;
        }
    }
    return false;
}

bool UnitMap::IsNaturalUnit() const
{
    if (!IsValid())
        return false;
    for (uint32_t i = static_cast<uint32_t>(ExprUnitType::Num); i <= static_cast<uint32_t>(ExprUnitType::Double); i++)
    {
        ExprUnitType type = static_cast<ExprUnitType>(i);
        if (GetUnitCount(type) != 0)
            return true;
    }
    return false;
}

bool UnitMap::MapHasType(const ExprUnitType& type)
{
    if (m_units.end() != m_units.find(type))
        return true;
    return false;
}

bool UnitMap::IsNumOrNatureUnit() const
{
    if (IsNum() || IsNaturalUnit())
        return true;
    return false;
}

bool UnitMap::IsCalculatable(const ExprUnitType& type)
{
    uint32_t i = static_cast<uint32_t>(type);
    if (static_cast<uint32_t>(ExprUnitType::Double) >= i && i >= static_cast<uint32_t>(ExprUnitType::Num))
        return true;
    return false;
}

bool UnitMap::IsNaturalUnit(const ExprUnitType& type)
{
    uint32_t i = static_cast<uint32_t>(type);
    if (static_cast<uint32_t>(ExprUnitType::Double) > i && i > static_cast<uint32_t>(ExprUnitType::Num))
        return true;
    return false;
}

ExprUnitType UnitMap::GetSingleUnit() const
{
    if (!IsValid())
        return ExprUnitType::Invalid;
    if (IsNum())
        return ExprUnitType::Num;
    ExprUnitType type = ExprUnitType::Double;
    bool isSet = false;
    for (auto unit = m_units.begin(); unit != m_units.end(); unit++)
    {
        if (unit->first == ExprUnitType::Num || unit->first == ExprUnitType::Int || unit->first == ExprUnitType::Double)
            continue;

        if (!IsZero(unit->second) && abs(unit->second - 1) > PRECISION_DEF)
            return ExprUnitType::Invalid;
        else if (abs(unit->second - 1) < PRECISION_DEF && isSet == false)
        {
            type = unit->first;
            isSet = true;
        }
        else if (abs(unit->second - 1) < PRECISION_DEF && isSet == true)
            return ExprUnitType::Invalid;
    }
    return type;
}

void UnitMap::Clear()
{
    m_units.clear();
}

void UnitMap::GetCopyFrom(const UnitMap& typeMap)
{
    Clear();
    for (auto it = typeMap.m_units.begin(); it != typeMap.m_units.end(); it++)
    {
        m_units.insert(std::pair<ExprUnitType, double>(it->first, it->second));
    }
    this->m_errorStr = typeMap.GetErrorStr();
}

bool UnitMap::UnitIsSame(const UnitMap& typeMap) const
{
    std::vector<ExprUnitType> types1;
    this->FindUnits(types1);
    std::vector<ExprUnitType> types2;
    typeMap.FindUnits(types2);
    if (types1.size() != types2.size())
        return false;
    if (types1.size() == 1)
    {
        // 支持数值与整数之间的相加
        if ((types1.at(0) == ExprUnitType::Int && types2.at(0) == ExprUnitType::Double)
            || (types1.at(0) == ExprUnitType::Double && types2.at(0) == ExprUnitType::Int))
        {
            return true;
        }
        if (types1.at(0) != types2.at(0))
            return false;
    }
    for (auto t = types1.begin(); t != types1.end(); t++)
    {
        if (*t == ExprUnitType::Num)
            continue;
        if (!IsZero(GetUnitCount(*t) - typeMap.GetUnitCount(*t)))
            return false;
    }
    return true;
}

double UnitMap::GetUnitCount(const ExprUnitType& type) const
{
    auto it = m_units.find(type);
    if (m_units.end() == it)
        return 0;
    else
        return it->second;
}

void UnitMap::FindUnits(std::vector<ExprUnitType>& types) const
{
    for (auto unit = m_units.begin(); unit != m_units.end(); unit++)
    {
        ExprUnitType type = unit->first;
        if (!IsZero(unit->second))
            types.push_back(type);
    }
}

void UnitMap::Pow(const double& value)
{
    for (std::map<ExprUnitType, double>::iterator it = m_units.begin();
        it != m_units.end();
        it++)
    {
        ExprUnitType type = it->first;
        if (!IsZero(it->second) && (IsNaturalUnit(type)))
            it->second = it->second * value;
    }
}

bool UnitMap::Multiply(const ExprUnitType& type, const double& value)
{
    if (!IsCalculatable(type))
        return false;
    if (type == ExprUnitType::Num)
    {
        if (m_units.size() > 0)
            return true;
    }
    else if (type == ExprUnitType::Int)
    {
        if (m_units.size() > 0)
            return true;
    }
    else if (type == ExprUnitType::Double)
    {
        if (IsNaturalUnit())
            return true;
        else if (IsNum())
            m_units.clear();
    }
    else
    {
        if (IsNum())
            m_units.clear();
        if (m_units.end() != m_units.find(ExprUnitType::Double))
            m_units.find(ExprUnitType::Double)->second = 0;
        else if (m_units.end() != m_units.find(ExprUnitType::Int))
            m_units.find(ExprUnitType::Int)->second = 0;
    }

    auto it = m_units.find(type);
    if (m_units.end() == it)
    {
        m_units.insert(std::pair<ExprUnitType, double>(type, value));
    }
    else
    {
        it->second = it->second + value;
        if (type == ExprUnitType::Num || type == ExprUnitType::Double)
            it->second = 1;
    }
    return true;
}
bool UnitMap::Divided(const ExprUnitType& type, const double& value)
{
    if (!IsCalculatable(type))
        return false;
    if (type == ExprUnitType::Double)
    {
        if (m_units.size() > 0)
            return true;
    }
    else if (type == ExprUnitType::Int)
    {
        if (m_units.size() > 0)
            return true;
    }
    else if (type == ExprUnitType::Num)
    {
        if (IsNumOrNatureUnit())
            return true;
    }
    else
    {
        if (IsNum())
            m_units.clear();
        if (m_units.end() != m_units.find(ExprUnitType::Double))
            m_units.find(ExprUnitType::Double)->second = 0;
        else if (m_units.end() != m_units.find(ExprUnitType::Int))
            m_units.find(ExprUnitType::Int)->second = 0;
    }
    auto it = m_units.find(type);
    if (m_units.end() == it)
    {
        double count = value;
        if (type == ExprUnitType::Num || type == ExprUnitType::Double)
            count = 1;
        m_units.insert(std::pair<ExprUnitType, double>(type, count));
    }
    else
    {
        it->second = it->second - value;
        if (type == ExprUnitType::Num || type == ExprUnitType::Double)
            it->second = 1;
    }
    return true;
}
bool UnitMap::Multiply(const UnitMap& typeMap)
{
    std::vector<ExprUnitType> unitTypes;
    typeMap.FindUnits(unitTypes);
    if (unitTypes.size() < 1)
        return true;
    for (auto t = unitTypes.begin(); t != unitTypes.end(); t++)
    {
        double count = typeMap.GetUnitCount(*t);
        if (!IsZero(count))
            Multiply(*t, count);
    }
    return true;
}
bool UnitMap::Divided(const UnitMap& typeMap)
{
    std::vector<ExprUnitType> unitTypes;
    typeMap.FindUnits(unitTypes);
    if (unitTypes.size() < 1)
        return true;
    for (auto t = unitTypes.begin(); t != unitTypes.end(); t++)
    {
        double count = typeMap.GetUnitCount(*t);
        if (!IsZero(count))
            Divided(*t, count);
    }
    return true;
}

bool UnitHelper::CheckUnitAdd(const UnitMap& left, const UnitMap& right, UnitMap& result)
{
    // 数值单位是不能相加
    if (left.IsNumOrNatureUnit() && right.IsNumOrNatureUnit())
    {
        if (left.UnitIsSame(right))
        {
            result.GetCopyFrom(left);
            return true;
        }
        else if (right.IsNum())
        {
            result.GetCopyFrom(left);
            return true;
        }
        else if (left.IsNum())
        {
            result.GetCopyFrom(right);
            return true;
        }
        else
            result.SetErrorStr(GBMP_TR(L"相加的单位不相同"));
    }
    else if (left.IsEmpty() && right.IsEmpty())
    {
        // 二者均无单位可相加，如字符串+字符串
        result.Clear();
        return true;
    }
    else
        result.SetErrorStr(GBMP_TR(L"单位不可计算"));
    return false;
}

bool UnitHelper::CheckUnitSubtract(const UnitMap& left, const UnitMap& right, UnitMap& result)
{
    if (left.IsNumOrNatureUnit() && right.IsNumOrNatureUnit())
    {
        if (left.UnitIsSame(right))
        {
            result.GetCopyFrom(left);
            return true;
        }
        else if (right.IsNum())
        {
            result.GetCopyFrom(left);
            return true;
        }
        else if (left.IsNum())
        {
            result.GetCopyFrom(right);
            return true;
        }
        else
            result.SetErrorStr(GBMP_TR(L"相减的单位不相同"));
    }
    else
        result.SetErrorStr(GBMP_TR(L"单位不可计算"));
    return false;
}

bool UnitHelper::CheckUnitMultiply(const UnitMap& left, const UnitMap& right, UnitMap& result)
{
    if (!left.IsValid() || !right.IsValid())
        return false;

    result.GetCopyFrom(left);
    if (result.Multiply(right))
        return true;
    result.SetErrorStr(GBMP_TR(L"相乘的单位不可计算"));
    return false;
}

bool UnitHelper::CheckUnitDivided(const UnitMap& left, const UnitMap& right, UnitMap& result)
{
    if (!left.IsValid() || !right.IsValid())
        return false;
    if (left.UnitIsSame(right))
    {
        result.InitialFrom(ExprUnitType::Num);
        return true;
    }
    result.GetCopyFrom(left);
    if (result.Divided(right))
        return true;
    result.SetErrorStr(GBMP_TR(L"相除的单位不可计算"));
    return false;
}

bool UnitHelper::CheckUnitModulus(const UnitMap& left, const UnitMap& right, UnitMap& result)
{
    result.Clear();
    if (!left.IsValid() || !right.IsValid())
        return false;

    if (left.UnitIsSame(right) || right.IsNum())
    {
        result.GetCopyFrom(left);
        return true;
    }
    else
        result.SetErrorStr(GBMP_TR(L"单位不可取余计算"));
    return false;
}

bool UnitHelper::CheckUnitCompare(const UnitMap& left, const UnitMap& right, UnitMap& result)
{
    if (left.UnitIsSame(right) ||
        (left.IsNaturalUnit() && right.IsNum()) ||
        (left.IsNum() && right.IsNaturalUnit()))
    {
        result.Clear();
        result.InitialFrom(ExprUnitType::Bool);
        return true;
    }
    else
        result.SetErrorStr(GBMP_TR(L"两单位不可进行比较"));
    return false;
}
